/*
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-07-14     Tian Yiqing  first implementation
 */

#include "drv_usart.h"

#ifdef RT_USING_SERIAL

#if !defined(BSP_USING_UART0) && !defined(BSP_USING_UART1) && \
    !defined(BSP_USING_UART2) && !defined(BSP_USING_UART3) && \
    !defined(BSP_USING_UART4) && !defined(BSP_USING_UART5) && \
    !defined(BSP_USING_UART6) && !defined(BSP_USING_UART7)
#error "Please define at least one UARTx"

#endif

#include <rtdevice.h>

#define USART_RX_IDLE_INT 1
#define USART_DMA_RX_COMPLETED 2
#define USART_DMA_TX_COMPLETED 3

// TODO: move to Kconfig
#define BSP_UART_RX_BUFSIZE 4096
#define BSP_UART_TX_BUFSIZE 4096

static void GD32_UART_IRQHandler(struct rt_serial_device *serial);

#ifdef RT_SERIAL_USING_DMA
static void GD32_UART_DMA_IRQHandler(struct rt_serial_device *serial, rt_uint8_t isr_flag);
#endif

enum
{
#ifdef BSP_USING_UART0
    UART0_INDEX,
#endif

#ifdef BSP_USING_UART1
    UART1_INDEX,
#endif

#ifdef BSP_USING_UART2
    UART2_INDEX,
#endif

#ifdef BSP_USING_UART3
    UART3_INDEX,
#endif

#ifdef BSP_USING_UART4
    UART4_INDEX,
#endif

#ifdef BSP_USING_UART5
    UART5_INDEX,
#endif

#ifdef BSP_USING_UART6
    UART6_INDEX,
#endif

#ifdef BSP_USING_UART7
    UART7_INDEX,
#endif
};


#if defined(BSP_USING_UART0)
struct rt_serial_device serial0;

void USART0_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_IRQHandler(&serial0);

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART0_RX_USING_DMA)
void DMA1_Channel2_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial0, USART_DMA_RX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_RX) && defined(BSP_UART0_RX_USING_DMA) */

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART0_TX_USING_DMA)
void DMA1_Channel7_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial0, USART_DMA_TX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_TX) && defined(BSP_UART0_TX_USING_DMA) */

#endif /* BSP_USING_UART0 */

#if defined(BSP_USING_UART1)
struct rt_serial_device serial1;

void USART1_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_IRQHandler(&serial1);

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_RX_USING_DMA)
void DMA0_Channel5_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial1, USART_DMA_RX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_RX) && defined(BSP_UART1_RX_USING_DMA) */

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_TX_USING_DMA)
void DMA0_Channel6_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial1, USART_DMA_TX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_TX) && defined(BSP_UART1_TX_USING_DMA) */

#endif /* BSP_USING_UART1 */

#if defined(BSP_USING_UART2)
struct rt_serial_device serial2;

void USART2_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_IRQHandler(&serial2);

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART2_RX_USING_DMA)
void DMA0_Channel1_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial2, USART_DMA_RX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_RX) && defined(BSP_UART2_RX_USING_DMA) */

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART2_TX_USING_DMA)
void DMA0_Channel3_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial2, USART_DMA_TX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_TX) && defined(BSP_UART2_TX_USING_DMA) */

#endif /* BSP_USING_UART2 */

#if defined(BSP_USING_UART3)
struct rt_serial_device serial3;

void UART3_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_IRQHandler(&serial3);

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART3_RX_USING_DMA)
void DMA0_Channel2_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial3, USART_DMA_RX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_RX) && defined(BSP_UART3_RX_USING_DMA) */

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART3_TX_USING_DMA)
void DMA0_Channel4_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial3, USART_DMA_TX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_TX) && defined(BSP_UART3_TX_USING_DMA) */

#endif /* BSP_USING_UART3 */

#if defined(BSP_USING_UART4)
struct rt_serial_device serial4;

void UART4_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_IRQHandler(&serial4);

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART4_RX_USING_DMA)
void DMA0_Channel0_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial4, USART_DMA_RX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_RX) && defined(BSP_UART4_RX_USING_DMA) */

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART4_TX_USING_DMA)
void DMA0_Channel7_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial4, USART_DMA_TX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_TX) && defined(BSP_UART4_TX_USING_DMA) */

#endif /* BSP_USING_UART4 */

#if defined(BSP_USING_UART5)
struct rt_serial_device serial5;

void USART5_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_IRQHandler(&serial5);

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART5_RX_USING_DMA)
void DMA1_Channel2_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial5, USART_DMA_RX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_RX) && defined(BSP_UART5_RX_USING_DMA) */

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART5_TX_USING_DMA)
void DMA1_Channel7_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial5, USART_DMA_TX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_TX) && defined(BSP_UART5_TX_USING_DMA) */

#endif /* BSP_USING_UART5 */

#if defined(BSP_USING_UART6)
struct rt_serial_device serial6;

void UART6_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_IRQHandler(&serial6);

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART6_RX_USING_DMA)
void DMA0_Channel3_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial6, USART_DMA_RX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_RX) && defined(BSP_UART6_RX_USING_DMA) */

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART6_TX_USING_DMA)
void DMA0_Channel1_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial6, USART_DMA_TX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_TX) && defined(BSP_UART6_TX_USING_DMA) */

#endif /* BSP_USING_UART6 */

#if defined(BSP_USING_UART7)
struct rt_serial_device serial7;

void UART7_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_IRQHandler(&serial7);

    /* leave interrupt */
    rt_interrupt_leave();
}

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART7_RX_USING_DMA)
void DMA0_Channel6_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial7, USART_DMA_RX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_RX) && defined(BSP_UART7_RX_USING_DMA) */

#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART7_TX_USING_DMA)
void DMA0_Channel0_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    GD32_UART_DMA_IRQHandler(&serial7, USART_DMA_TX_COMPLETED);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* defined(BSP_UART_USING_DMA_TX) && defined(BSP_UART7_TX_USING_DMA) */

#endif /* BSP_USING_UART7 */

static struct gd32_uart uart_obj[] = {
    #ifdef BSP_USING_UART0
    {
        USART0,                                 // uart peripheral index
        USART0_IRQn,                            // uart iqrn
        RCU_USART0, RCU_GPIOA, RCU_GPIOA,       // periph clock, tx gpio clock, rt gpio clock
#if defined SOC_SERIES_GD32F4xx
        GPIOA, GPIO_AF_7, GPIO_PIN_9,           // tx port, tx alternate, tx pin
        GPIOA, GPIO_AF_7, GPIO_PIN_10,          // rx port, rx alternate, rx pin
#else
        GPIOA, GPIO_PIN_9,           // tx port, tx pin
        GPIOA, GPIO_PIN_10,          // rx port, rx pin
#endif
        &serial0,
        "uart0",
    },
    #endif

    #ifdef BSP_USING_UART1
    {
        USART1,                                 // uart peripheral index
        USART1_IRQn,                            // uart iqrn
        RCU_USART1, RCU_GPIOA, RCU_GPIOA,       // periph clock, tx gpio clock, rt gpio clock
#if defined SOC_SERIES_GD32F4xx
        GPIOA, GPIO_AF_7, GPIO_PIN_2,           // tx port, tx alternate, tx pin
        GPIOA, GPIO_AF_7, GPIO_PIN_3,           // rx port, rx alternate, rx pin
#else
        GPIOA, GPIO_PIN_2,                      // tx port, tx pin
        GPIOA, GPIO_PIN_3,                      // rx port, rx pin
#endif
        &serial1,
        "uart1",
    },
    #endif

    #ifdef BSP_USING_UART2
    {
        USART2,                                 // uart peripheral index
        USART2_IRQn,                            // uart iqrn
        RCU_USART2, RCU_GPIOB, RCU_GPIOB,       // periph clock, tx gpio clock, rt gpio clock
#if defined SOC_SERIES_GD32F4xx
        GPIOB, GPIO_AF_7, GPIO_PIN_10,          // tx port, tx alternate, tx pin
        GPIOB, GPIO_AF_7, GPIO_PIN_11,          // rx port, rx alternate, rx pin
#else
        GPIOB, GPIO_PIN_10,          // tx port, tx pin
        GPIOB, GPIO_PIN_11,          // rx port, rx pin
#endif
        &serial2,
        "uart2",
    },
    #endif

    #ifdef BSP_USING_UART3
    {
        UART3,                                 // uart peripheral index
        UART3_IRQn,                            // uart iqrn
        RCU_UART3, RCU_GPIOC, RCU_GPIOC,       // periph clock, tx gpio clock, rt gpio clock
#if defined SOC_SERIES_GD32F4xx
        GPIOC, GPIO_AF_8, GPIO_PIN_10,         // tx port, tx alternate, tx pin
        GPIOC, GPIO_AF_8, GPIO_PIN_11,         // rx port, rx alternate, rx pin
#else
        GPIOC, GPIO_PIN_10,         // tx port, tx pin
        GPIOC, GPIO_PIN_11,         // rx port, rx pin
#endif
        &serial3,
        "uart3",
    },
    #endif

    #ifdef BSP_USING_UART4
    {
        UART4,                                 // uart peripheral index
        UART4_IRQn,                            // uart iqrn
        RCU_UART4, RCU_GPIOC, RCU_GPIOD,       // periph clock, tx gpio clock, rt gpio clock
#if defined SOC_SERIES_GD32F4xx
        GPIOC, GPIO_AF_8, GPIO_PIN_12,         // tx port, tx alternate, tx pin
        GPIOD, GPIO_AF_8, GPIO_PIN_2,          // rx port, rx alternate, rx pin
#else
        GPIOC, GPIO_PIN_12,         // tx port, tx pin
        GPIOD, GPIO_PIN_2,          // rx port, rx pin
#endif
        &serial4,
        "uart4",
    },
    #endif

    #ifdef BSP_USING_UART5
    {
        USART5,                                 // uart peripheral index
        USART5_IRQn,                            // uart iqrn
        RCU_USART5, RCU_GPIOC, RCU_GPIOC,       // periph clock, tx gpio clock, rt gpio clock
#if defined SOC_SERIES_GD32F4xx
        GPIOC, GPIO_AF_8, GPIO_PIN_6,           // tx port, tx alternate, tx pin
        GPIOC, GPIO_AF_8, GPIO_PIN_7,           // rx port, rx alternate, rx pin
#else
        GPIOC, GPIO_PIN_6,           // tx port, tx pin
        GPIOC, GPIO_PIN_7,           // rx port, rx pin
#endif
        &serial5,
        "uart5",
    },
    #endif

    #ifdef BSP_USING_UART6
    {
        UART6,                                 // uart peripheral index
        UART6_IRQn,                            // uart iqrn
        RCU_UART6, RCU_GPIOE, RCU_GPIOE,       // periph clock, tx gpio clock, rt gpio clock
#if defined SOC_SERIES_GD32F4xx
        GPIOE, GPIO_AF_8, GPIO_PIN_7,          // tx port, tx alternate, tx pin
        GPIOE, GPIO_AF_8, GPIO_PIN_8,          // rx port, rx alternate, rx pin
#else
        GPIOE, GPIO_PIN_7,          // tx port, tx pin
        GPIOE, GPIO_PIN_8,          // rx port, rx pin
#endif
        &serial6,
        "uart6",
    },
    #endif

    #ifdef BSP_USING_UART7
    {
        UART7,                                 // uart peripheral index
        UART7_IRQn,                            // uart iqrn
        RCU_UART7, RCU_GPIOE, RCU_GPIOE,       // periph clock, tx gpio clock, rt gpio clock
#if defined SOC_SERIES_GD32F4xx
        GPIOE, GPIO_AF_8, GPIO_PIN_0,          // tx port, tx alternate, tx pin
        GPIOE, GPIO_AF_8, GPIO_PIN_1,          // rx port, rx alternate, rx pin
#else
        GPIOE, GPIO_PIN_0,          // tx port, tx pin
        GPIOE, GPIO_PIN_1,          // rx port, rx pin
#endif
        &serial7,
        "uart7",
    },
    #endif
};

#ifdef RT_SERIAL_USING_DMA

static struct dma_config uart_dma_rx_config[] = {
    #ifdef BSP_USING_UART0
    {
        DMA1,
        DMA1_Channel2_IRQn,
        RCU_DMA1,
        DMA_CH2,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART1
    {
        DMA0,
        DMA0_Channel5_IRQn,
        RCU_DMA0,
        DMA_CH5,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART2
    {
        DMA0,
        DMA0_Channel1_IRQn,
        RCU_DMA0,
        DMA_CH1,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART3
    {
        DMA0,
        DMA0_Channel2_IRQn,
        RCU_DMA0,
        DMA_CH2,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART4
    {
        DMA0,
        DMA0_Channel0_IRQn,
        RCU_DMA0,
        DMA_CH0,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART5
    {
        DMA1,
        DMA1_Channel2_IRQn,
        RCU_DMA1,
        DMA_CH2,
        DMA_SUBPERI5,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART6
    {
        DMA0,
        DMA0_Channel3_IRQn,
        RCU_DMA0,
        DMA_CH3,
        DMA_SUBPERI5,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART7
    {
        DMA0,
        DMA0_Channel6_IRQn,
        RCU_DMA0,
        DMA_CH6,
        DMA_SUBPERI5,
        DMA_PRIORITY_MEDIUM
    },
    #endif
};

static struct dma_config uart_dma_tx_config[] = {
    #ifdef BSP_USING_UART0
    {
        DMA1,
        DMA1_Channel7_IRQn,
        RCU_DMA1,
        DMA_CH7,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART1
    {
        DMA0,
        DMA0_Channel6_IRQn,
        RCU_DMA0,
        DMA_CH6,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART2
    {
        DMA0,
        DMA0_Channel3_IRQn,
        RCU_DMA0,
        DMA_CH3,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART3
    {
        DMA0,
        DMA0_Channel4_IRQn,
        RCU_DMA0,
        DMA_CH4,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART4
    {
        DMA0,
        DMA0_Channel7_IRQn,
        RCU_DMA0,
        DMA_CH7,
        DMA_SUBPERI4,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART5
    {
        DMA1,
        DMA1_Channel7_IRQn,
        RCU_DMA1,
        DMA_CH7,
        DMA_SUBPERI5,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART6
    {
        DMA0,
        DMA0_Channel1_IRQn,
        RCU_DMA0,
        DMA_CH1,
        DMA_SUBPERI5,
        DMA_PRIORITY_MEDIUM
    },
    #endif
    #ifdef BSP_USING_UART7
    {
        DMA0,
        DMA0_Channel0_IRQn,
        RCU_DMA0,
        DMA_CH0,
        DMA_SUBPERI5,
        DMA_PRIORITY_MEDIUM
    },
    #endif
};

static void gd32_dma_disable(struct rt_serial_device *serial, rt_ubase_t flag)
{
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(flag == RT_DEVICE_FLAG_DMA_TX || flag == RT_DEVICE_FLAG_DMA_RX);

    uart = (struct gd32_uart *)serial->parent.user_data;

    struct dma_config *dma_config;
    if (RT_DEVICE_FLAG_DMA_RX == flag)
    {
        dma_config = uart->dma_rx;
        usart_dma_receive_config(uart->uart_periph, USART_RECEIVE_DMA_DISABLE);    
    }
    else
    {
        dma_config = uart->dma_tx;
        usart_dma_transmit_config(uart->uart_periph, USART_TRANSMIT_DMA_DISABLE);
    }

    if(dma_config != NULL)
        dma_channel_disable(dma_config->dma_periph, dma_config->channel);
}

static void gd32_dma_config(struct rt_serial_device *serial, rt_ubase_t flag)
{
    struct gd32_uart *uart;
    struct dma_config *dma_config;
    dma_single_data_parameter_struct dma_init_struct;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(flag == RT_DEVICE_FLAG_DMA_TX || flag == RT_DEVICE_FLAG_DMA_RX);
    
    uart = (struct gd32_uart *)serial->parent.user_data;

    if (RT_DEVICE_FLAG_DMA_RX == flag)
    {
        dma_config = uart->dma_rx;
    }
    else /* RT_DEVICE_FLAG_DMA_TX == flag */
    {
        dma_config = uart->dma_tx;
    }

    rcu_periph_clock_enable(dma_config->dma_clk);    

    /* deinitialize DMA channel */
    dma_single_data_para_struct_init(&dma_init_struct);
    dma_deinit(dma_config->dma_periph, dma_config->channel);
    if (RT_DEVICE_FLAG_DMA_RX == flag)
    {
        struct rt_serial_rx_fifo *rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx;
        dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
        dma_init_struct.memory0_addr = (uint32_t)rx_fifo->buffer;
        dma_init_struct.number = serial->config.rx_bufsz;
    }
    else
    {
        dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
        dma_init_struct.memory0_addr = 0;
        dma_init_struct.number = 0;
    }  
    
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
    dma_init_struct.periph_addr = (uint32_t)&USART_DATA(uart->uart_periph);;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.priority = dma_config->priority;
    dma_single_data_mode_init(dma_config->dma_periph, dma_config->channel, &dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(dma_config->dma_periph, dma_config->channel);
    
    dma_channel_subperipheral_select(dma_config->dma_periph, dma_config->channel, dma_config->subperiph);

    
    if (RT_DEVICE_FLAG_DMA_RX == flag)
    {
        NVIC_SetPriority(dma_config->irqn, 0);
        NVIC_EnableIRQ(dma_config->irqn);

        dma_config->last_index = 0;
        usart_dma_receive_config(uart->uart_periph, USART_RECEIVE_DMA_ENABLE);

        /* enable DMA channel transfer complete interrupt */
        dma_interrupt_enable(dma_config->dma_periph, dma_config->channel, DMA_CHXCTL_FTFIE);
        /* enable DMA channel */
        dma_channel_enable(dma_config->dma_periph, dma_config->channel);

        usart_interrupt_enable(uart->uart_periph, USART_INT_IDLE);
    }
    else
    {
        usart_flag_clear(uart->uart_periph, USART_FLAG_TC);
        usart_interrupt_enable(uart->uart_periph, USART_INT_TC);
    }    
}

static void GD32_UART_DMA_IRQHandler(struct rt_serial_device *serial,
                                     rt_uint8_t isr_flag) 
{
    struct gd32_uart *uart = (struct gd32_uart *)serial->parent.user_data;

    RT_ASSERT(uart != RT_NULL);

    rt_base_t level;
    rt_size_t recv_len, counter, total_index;

    struct dma_config *dma_config;
    if (isr_flag == USART_DMA_TX_COMPLETED)
        dma_config = uart->dma_tx;
    else
        dma_config = uart->dma_rx;

    switch (isr_flag) 
    {
    case USART_RX_IDLE_INT: 
        {
            level = rt_hw_interrupt_disable();
            counter = dma_transfer_number_get(dma_config->dma_periph, dma_config->channel);
            total_index = uart->serial->config.rx_bufsz - counter;

            if (total_index >= dma_config->last_index)
                recv_len = total_index - dma_config->last_index;
            else
                recv_len = total_index + (serial->config.rx_bufsz - dma_config->last_index);

            if (recv_len > 0 && recv_len <= serial->config.rx_bufsz) 
            {
                if (counter) 
                {
                    dma_config->last_index = total_index;
                } 
                else 
                {
                    dma_config->last_index = 0;
                }
                rt_hw_interrupt_enable(level);

                rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8));
            } 
            else 
            {
                rt_hw_interrupt_enable(level);
            }
        } 
        break;
    case USART_DMA_RX_COMPLETED:
        if (dma_interrupt_flag_get(dma_config->dma_periph, dma_config->channel,
                                    DMA_INT_FLAG_FTF) != RESET) 
        {
            dma_interrupt_flag_clear(dma_config->dma_periph, dma_config->channel,
                                    DMA_INT_FLAG_FTF);

            usart_dma_receive_config(uart->uart_periph, USART_RECEIVE_DMA_DISABLE);
            dma_channel_disable(dma_config->dma_periph, dma_config->channel);
            dma_transfer_number_config(dma_config->dma_periph, dma_config->channel,
                                        uart->serial->config.rx_bufsz);
            dma_channel_enable(dma_config->dma_periph, dma_config->channel);
            usart_dma_receive_config(uart->uart_periph, USART_RECEIVE_DMA_ENABLE);
        }
        break;
    case USART_DMA_TX_COMPLETED:
        break;
    default:
        break;
    }
}

#endif

/**
* @brief UART MSP Initialization
*        This function configures the hardware resources used in this example:
*           - Peripheral's clock enable
*           - Peripheral's GPIO Configuration
*           - NVIC configuration for UART interrupt request enable
* @param huart: UART handle pointer
* @retval None
*/
void gd32_uart_gpio_init(struct gd32_uart *uart)
{
    /* enable USART clock */
    rcu_periph_clock_enable(uart->tx_gpio_clk);
    rcu_periph_clock_enable(uart->rx_gpio_clk);
    rcu_periph_clock_enable(uart->per_clk);

#if defined SOC_SERIES_GD32F4xx
    /* connect port to USARTx_Tx */
    gpio_af_set(uart->tx_port, uart->tx_af, uart->tx_pin);

    /* connect port to USARTx_Rx */
    gpio_af_set(uart->rx_port, uart->rx_af, uart->rx_pin);

    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(uart->tx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, uart->tx_pin);
    gpio_output_options_set(uart->tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, uart->tx_pin);

    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(uart->rx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, uart->rx_pin);
    gpio_output_options_set(uart->rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, uart->rx_pin);

#else
    /* connect port to USARTx_Tx */
    gpio_init(uart->tx_port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, uart->tx_pin);

    /* connect port to USARTx_Rx */
    gpio_init(uart->rx_port, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, uart->rx_pin);
#endif

    NVIC_SetPriority(uart->irqn, 0);
    NVIC_EnableIRQ(uart->irqn);
}

/**
  * @brief  uart configure
  * @param  serial, cfg
  * @retval None
  */
static rt_err_t gd32_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart = (struct gd32_uart *)serial->parent.user_data;

    gd32_uart_gpio_init(uart);

    usart_baudrate_set(uart->uart_periph, cfg->baud_rate);

    switch (cfg->data_bits)
    {
    case DATA_BITS_9:
        usart_word_length_set(uart->uart_periph, USART_WL_9BIT);
        break;

    default:
        usart_word_length_set(uart->uart_periph, USART_WL_8BIT);
        break;
    }

    switch (cfg->stop_bits)
    {
    case STOP_BITS_2:
        usart_stop_bit_set(uart->uart_periph, USART_STB_2BIT);
        break;
    default:
        usart_stop_bit_set(uart->uart_periph, USART_STB_1BIT);
        break;
    }

    switch (cfg->parity)
    {
    case PARITY_ODD:
        usart_parity_config(uart->uart_periph, USART_PM_ODD);
        break;
    case PARITY_EVEN:
        usart_parity_config(uart->uart_periph, USART_PM_EVEN);
        break;
    default:
        usart_parity_config(uart->uart_periph, USART_PM_NONE);
        break;
    }

    usart_receive_config(uart->uart_periph, USART_RECEIVE_ENABLE);
    usart_transmit_config(uart->uart_periph, USART_TRANSMIT_ENABLE);
    usart_enable(uart->uart_periph);

    return RT_EOK;
}

/**
  * @brief  uart control
  * @param  serial, arg
  * @retval None
  */
static rt_err_t gd32_uart_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct gd32_uart *uart;

    rt_ubase_t ctrl_arg = (rt_ubase_t)arg;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct gd32_uart *)serial->parent.user_data;

    if(ctrl_arg & (RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_RX_NON_BLOCKING))
    {
        if (uart->uart_dma_flag & RT_DEVICE_FLAG_DMA_RX)
            ctrl_arg = RT_DEVICE_FLAG_DMA_RX;
        else
            ctrl_arg = RT_DEVICE_FLAG_INT_RX;
    }
    else if(ctrl_arg & (RT_DEVICE_FLAG_TX_BLOCKING | RT_DEVICE_FLAG_TX_NON_BLOCKING))
    {
        if (uart->uart_dma_flag & RT_DEVICE_FLAG_DMA_TX)
            ctrl_arg = RT_DEVICE_FLAG_DMA_TX;
        else
            ctrl_arg = RT_DEVICE_FLAG_INT_TX;
    }

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable rx irq */
        NVIC_DisableIRQ(uart->irqn);
        /* disable interrupt */
        if(ctrl_arg == RT_DEVICE_FLAG_INT_RX)
            usart_interrupt_disable(uart->uart_periph, USART_INT_RBNE);
        else if(ctrl_arg == RT_DEVICE_FLAG_INT_TX)
            usart_interrupt_disable(uart->uart_periph, USART_INT_TBE);
    #ifdef RT_SERIAL_USING_DMA
        else if(ctrl_arg == RT_DEVICE_FLAG_DMA_RX)
        {
            usart_interrupt_disable(uart->uart_periph, USART_INT_RBNE);
            dma_interrupt_disable(uart->dma_rx->dma_periph, uart->dma_rx->channel, DMA_CHXCTL_FTFIE);

            usart_dma_receive_config(uart->uart_periph, USART_RECEIVE_DMA_DISABLE);
            dma_channel_disable(uart->dma_rx->dma_periph, uart->dma_rx->channel);
        }
        else if(ctrl_arg == RT_DEVICE_FLAG_DMA_TX)
        {
            usart_interrupt_disable(uart->uart_periph, USART_INT_TC);
            dma_interrupt_disable(uart->dma_tx->dma_periph, uart->dma_tx->channel, DMA_CHXCTL_FTFIE);

            usart_dma_transmit_config(uart->uart_periph, USART_TRANSMIT_DMA_DISABLE);
            dma_channel_disable(uart->dma_tx->dma_periph, uart->dma_tx->channel);
        }
    #endif
        break;
    case RT_DEVICE_CTRL_SET_INT:
        /* enable rx irq */
        NVIC_EnableIRQ(uart->irqn);
        /* enable interrupt */
        if(ctrl_arg == RT_DEVICE_FLAG_INT_RX)
            usart_interrupt_enable(uart->uart_periph, USART_INT_RBNE);
        else if(ctrl_arg == RT_DEVICE_FLAG_INT_TX)
            usart_interrupt_enable(uart->uart_periph, USART_INT_TBE);
        break;
    case RT_DEVICE_CTRL_CONFIG:
        if (ctrl_arg & (RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX))
        {

#ifdef RT_SERIAL_USING_DMA
            gd32_dma_config(serial, ctrl_arg);
#endif
        }
        else
            gd32_uart_control(serial, RT_DEVICE_CTRL_SET_INT, (void *)arg);
        break;
    case RT_DEVICE_CHECK_OPTMODE:
        if(ctrl_arg & RT_DEVICE_FLAG_DMA_TX)
            return RT_SERIAL_TX_BLOCKING_NO_BUFFER;
        else
            return RT_SERIAL_TX_BLOCKING_BUFFER;
    case RT_DEVICE_CTRL_CLOSE: //rt_serial_close will call it, also it can be reinit when next opening
        usart_receive_config(uart->uart_periph, USART_RECEIVE_DISABLE);
        usart_transmit_config(uart->uart_periph, USART_TRANSMIT_DISABLE);
        usart_disable(uart->uart_periph);

#ifdef RT_SERIAL_USING_DMA
        gd32_dma_disable(serial, RT_DEVICE_FLAG_DMA_RX);
        gd32_dma_disable(serial, RT_DEVICE_FLAG_DMA_TX);
#endif
        break;
    }

    return RT_EOK;
}

/**
  * @brief  uart put char
  * @param  serial, ch
  * @retval None
  */
static int gd32_uart_putc(struct rt_serial_device *serial, char ch)
{
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct gd32_uart *)serial->parent.user_data;

    usart_data_transmit(uart->uart_periph, ch);
    while((usart_flag_get(uart->uart_periph, USART_FLAG_TBE) == RESET));

    return RT_EOK;
}

/**
  * @brief  uart get char
  * @param  serial
  * @retval None
  */
static int gd32_uart_getc(struct rt_serial_device *serial)
{
    int ch;
    struct gd32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct gd32_uart *)serial->parent.user_data;

    ch = -1;
    if (usart_flag_get(uart->uart_periph, USART_FLAG_RBNE) != RESET)
        ch = usart_data_receive(uart->uart_periph);
    return ch;
}

static rt_size_t gd32_uart_transmit(struct rt_serial_device *serial,
                                    rt_uint8_t *buf, rt_size_t size,
                                    rt_uint32_t tx_flag) 
{
    RT_ASSERT(serial != RT_NULL);
    
#ifdef RT_SERIAL_USING_DMA
    struct gd32_uart *uart;
    uart = (struct gd32_uart *)serial->parent.user_data;
	
    if (uart->uart_dma_flag & RT_DEVICE_FLAG_DMA_TX)
    {
        struct dma_config *dma_config = uart->dma_tx;
        dma_single_data_parameter_struct dma_init_struct;
        dma_single_data_para_struct_init(&dma_init_struct);
        dma_deinit(dma_config->dma_periph, dma_config->channel);
        dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
        dma_init_struct.memory0_addr = (uint32_t)buf;
        dma_init_struct.number = size;    
        dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
        dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
        dma_init_struct.periph_addr = (uint32_t)&USART_DATA(uart->uart_periph);;
        dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
        dma_init_struct.priority = dma_config->priority;
        dma_single_data_mode_init(dma_config->dma_periph, dma_config->channel, &dma_init_struct);

        /* configure DMA mode */
        dma_circulation_disable(dma_config->dma_periph, dma_config->channel);  
        dma_channel_subperipheral_select(dma_config->dma_periph, dma_config->channel, dma_config->subperiph);

        /* enable DMA channel */
        dma_channel_enable(dma_config->dma_periph, dma_config->channel);
        /* USART DMA enable for transmission and reception */
        usart_dma_transmit_config(uart->uart_periph, USART_TRANSMIT_DMA_ENABLE);

        return size;
    }
#endif

    gd32_uart_control(serial, RT_DEVICE_CTRL_SET_INT, (void *)tx_flag);

    return size;
}

/**
 * Uart common interrupt process. This need add to uart ISR.
 *
 * @param serial serial device
 */
static void GD32_UART_IRQHandler(struct rt_serial_device *serial)
{
    struct gd32_uart *uart = (struct gd32_uart *) serial->parent.user_data;

    RT_ASSERT(uart != RT_NULL);

    /* UART in mode Receiver -------------------------------------------------*/
    if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_RBNE) != RESET) &&
            (usart_flag_get(uart->uart_periph, USART_FLAG_RBNE) != RESET))
    {
        /* Clear RXNE interrupt flag */
        usart_flag_clear(uart->uart_periph, USART_FLAG_RBNE);

        struct rt_serial_rx_fifo *rx_fifo;
        rx_fifo = (struct rt_serial_rx_fifo *)serial->serial_rx;
        RT_ASSERT(rx_fifo != RT_NULL);

        rt_ringbuffer_putchar(&rx_fifo->rb, usart_data_receive(uart->uart_periph));

        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
        
    } 
    else if((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_TBE) != RESET) &&
            (usart_flag_get(uart->uart_periph, USART_FLAG_TBE) != RESET))
    {
        usart_flag_clear(uart->uart_periph, USART_FLAG_TBE);

        struct rt_serial_tx_fifo *tx_fifo;
        tx_fifo = (struct rt_serial_tx_fifo *)serial->serial_tx;
        RT_ASSERT(tx_fifo != RT_NULL);

        rt_uint8_t put_char = 0;
        if(rt_ringbuffer_getchar(&tx_fifo->rb, &put_char))
        {
            usart_data_transmit(uart->uart_periph, put_char);
        }
        else
        {
            usart_interrupt_disable(uart->uart_periph, USART_INT_TBE);
            usart_interrupt_enable(uart->uart_periph, USART_INT_TC);
        }
    } 
    else if((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_TC) != RESET) &&
            (usart_flag_get(uart->uart_periph, USART_FLAG_TC) != RESET))
    {
        usart_flag_clear(uart->uart_periph, USART_FLAG_TC);
#ifdef RT_SERIAL_USING_DMA
        if (uart->uart_dma_flag & RT_DEVICE_FLAG_DMA_TX)
        {
            rt_size_t trans_total_index;
            rt_base_t level = rt_hw_interrupt_disable();
            trans_total_index = dma_transfer_number_get(uart->dma_tx->dma_periph, uart->dma_tx->channel);
            rt_hw_interrupt_enable(level);

            if(trans_total_index == 0)
                rt_hw_serial_isr(uart->serial, RT_SERIAL_EVENT_TX_DMADONE);
        }
        else
#endif
        {
            usart_interrupt_disable(uart->uart_periph, USART_INT_TC);
            rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE);
        }
    } 
#ifdef RT_SERIAL_USING_DMA
    else if ((uart->uart_dma_flag) && (usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_IDLE) != RESET) &&
            (usart_flag_get(uart->uart_periph, USART_FLAG_IDLE) != RESET))
    {
        /* clear IDLE flag */
        // usart_flag_clear(uart->uart_periph, USART_FLAG_IDLE);
        usart_data_receive(uart->uart_periph);

        GD32_UART_DMA_IRQHandler(serial, USART_RX_IDLE_INT);
    }
#endif
    else if(usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_ERR_ORERR) != RESET)
    {
        usart_data_receive(uart->uart_periph);
        usart_flag_clear(uart->uart_periph, USART_FLAG_ORERR);
    }
    else
    {
        if(usart_flag_get(uart->uart_periph, USART_FLAG_CTS) != RESET)
        {
            usart_flag_clear(uart->uart_periph, USART_FLAG_CTS);
        }
        if(usart_flag_get(uart->uart_periph, USART_FLAG_LBD) != RESET)
        {
            usart_flag_clear(uart->uart_periph, USART_FLAG_LBD);
        }
        if(usart_flag_get(uart->uart_periph, USART_FLAG_TBE) != RESET)
        {
            usart_flag_clear(uart->uart_periph, USART_FLAG_TBE);
        }
        if(usart_flag_get(uart->uart_periph, USART_FLAG_TC) != RESET)
        {
            usart_flag_clear(uart->uart_periph, USART_FLAG_TC);
        }
        if(usart_flag_get(uart->uart_periph, USART_FLAG_RBNE) != RESET)
        {
            usart_flag_clear(uart->uart_periph, USART_FLAG_RBNE);
        }
        if(usart_flag_get(uart->uart_periph, USART_FLAG_IDLE) != RESET)
        {
            usart_flag_clear(uart->uart_periph, USART_FLAG_IDLE);
        }
        if(usart_flag_get(uart->uart_periph, USART_FLAG_PERR) != RESET)
        {
            usart_flag_clear(uart->uart_periph, USART_FLAG_PERR);
        }
        if(usart_flag_get(uart->uart_periph, USART_FLAG_EB) != RESET)
        {
            usart_flag_clear(uart->uart_periph, USART_FLAG_EB);
        }
    }

}

static const struct rt_uart_ops gd32_uart_ops =
{
    .configure = gd32_uart_configure,
    .control = gd32_uart_control,
    .putc = gd32_uart_putc,
    .getc = gd32_uart_getc,
    .transmit = gd32_uart_transmit,
};

static void gd32_uart_get_config(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

#ifdef BSP_USING_UART0
    uart_obj[UART0_INDEX].serial->config = config;
    uart_obj[UART0_INDEX].uart_dma_flag = 0;
    uart_obj[UART0_INDEX].serial->config.rx_bufsz = BSP_UART_RX_BUFSIZE;
    uart_obj[UART0_INDEX].serial->config.tx_bufsz = BSP_UART_TX_BUFSIZE;

    #ifdef BSP_UART0_RX_USING_DMA
    uart_obj[UART0_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_RX;
    uart_obj[UART0_INDEX].dma_rx = &uart_dma_rx_config[UART0_INDEX];
    #endif

    #ifdef BSP_UART0_TX_USING_DMA
    uart_obj[UART0_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_TX;
    uart_obj[UART0_INDEX].dma_tx = &uart_dma_tx_config[UART0_INDEX];
    #endif
#endif

#ifdef BSP_USING_UART1
    uart_obj[UART1_INDEX].serial->config = config;
    uart_obj[UART1_INDEX].uart_dma_flag = 0;
    uart_obj[UART1_INDEX].serial->config.rx_bufsz = BSP_UART_RX_BUFSIZE;
    uart_obj[UART1_INDEX].serial->config.tx_bufsz = BSP_UART_TX_BUFSIZE;

    #ifdef BSP_UART1_RX_USING_DMA
    uart_obj[UART1_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_RX;
    uart_obj[UART1_INDEX].dma_rx = &uart_dma_rx_config[UART1_INDEX];
    #endif

    #ifdef BSP_UART1_TX_USING_DMA
    uart_obj[UART1_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_TX;
    uart_obj[UART1_INDEX].dma_tx = &uart_dma_tx_config[UART1_INDEX];
    #endif
#endif
}

/**
  * @brief  uart init
  * @param  None
  * @retval None
  */
int rt_hw_usart_init(void)
{
    
    int i;

    int result;

    gd32_uart_get_config();
    for (i = 0; i < sizeof(uart_obj) / sizeof(uart_obj[0]); i++)
    {
        uart_obj[i].serial->ops    = &gd32_uart_ops;
        
        /* register UART1 device */
        result = rt_hw_serial_register(uart_obj[i].serial,
                              uart_obj[i].device_name,
                              RT_DEVICE_FLAG_RDWR,
                              (void *)&uart_obj[i]);
        RT_ASSERT(result == RT_EOK);
    }

    return result;
}

INIT_BOARD_EXPORT(rt_hw_usart_init);

#endif
